package com.mazaiting.gateway.sentinel.write;

import com.alibaba.cloud.sentinel.datasource.config.NacosDataSourceProperties;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;

import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 同步流控规则至 nacos
 * 修改规则会走执行 ModifyRulesCommandHandler#handle() 方法
 *
 * @author mazaiting
 * @create_time 2022/10/14 01:21
 * @see com.alibaba.csp.sentinel.command.handler.ModifyRulesCommandHandler#handle(CommandRequest)
 */
@Slf4j
public class NacosWritableDataSource<T> implements WritableDataSource<T> {
    /**
     * nacos 数据源配置
     */
    private final NacosDataSourceProperties nacosDataSourceProperties;
    /**
     * 配置服务
     */
    private ConfigService configService;
    /**
     * 配置转换
     */
    private final Converter<T, String> configEncoder;
    /**
     * 锁
     */
    private final Lock lock;

    public NacosWritableDataSource(NacosDataSourceProperties nacosDataSourceProperties, Converter<T, String> configEncoder) {
        this.lock = new ReentrantLock(true);
        this.nacosDataSourceProperties = nacosDataSourceProperties;
        this.configEncoder = configEncoder;
        initConfigService();
    }

    /**
     * 初始化配置服务
     */
    private void initConfigService() {
        try {
            this.configService = NacosFactory.createConfigService(buildProperties());
        } catch (NacosException e) {
            log.error("获取 nacos 服务异常: {}", e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 构建属性
     * @return 属性
     */
    private Properties buildProperties() {
        Properties properties = new Properties();
        // 服务地址
        properties.setProperty(PropertyKeyConst.SERVER_ADDR, nacosDataSourceProperties.getServerAddr());
        // 命名空间
        properties.setProperty(PropertyKeyConst.NAMESPACE, nacosDataSourceProperties.getNamespace());
        return properties;
    }

    @Override
    public void write(T t) throws Exception {
        lock.lock();
        try {
            // 发布到配置中心
            configService.publishConfig(nacosDataSourceProperties.getDataId(), nacosDataSourceProperties.getGroupId(),
                    this.configEncoder.convert(t), ConfigType.JSON.getType());
        } catch (Exception e) {
            log.error("发布配置中心失败: {}", e.getMessage());
            throw e;
        } finally {
            this.lock.unlock();
        }
    }

    @Override
    public void close() throws Exception {
        // nothing
    }

}
